#include <tools.h>
#include <bigimage/perspectivecamera.h>

#include "editorscenemgr.h"
#include "terrain/terrainobject.h"
#include "scene/editorscene.h"
#include "scene/sceneobjectnavigationmap.h"
#include "helper/terrainhelperobject.h"
#include "helper/navigationhelperobject.h"
#include "data/sceneobjectinfo.h"

#include "sceneobjects/sceneobjectmanager.h"

#include "operator/operator.h"
#include "operator/terrainnull0operator.h"
#include "operator/terrainsurfaceoperator.h"
#include "operator/terrainblockheightoperator.h"
#include "operator/sceneobjectoperator.h"
#include "operator/terrainblockcoloroperator.h"
#include "operator/navigationmapeditoperator.h"

#include "command/commandmgr.h"


CEditorSceneMgr::CEditorSceneMgr(){

}
CEditorSceneMgr::~CEditorSceneMgr(){
    CScene::m_pCurrentActiveScene.Detach();
}
void CEditorSceneMgr::Init( CTextureTarget* pTarget, float fViewportWidth, float fViewportHeight, const char* strResourceSpace, int32 nBlockCountX, int32 nBlockCountY, float fBlockSize, float fObjectUnitSize, const char* strSkyBox ){
    char _strTextBuff[256];

    m_pTextureTarget = pTarget;
    m_fWidth = fViewportWidth;
    m_fHeight = fViewportHeight;
    m_strResourceSpace = strResourceSpace;

    CEditorScene* _pEditorScene = NEW CEditorScene();
    m_rEditorScene = _pEditorScene;

    CScene::m_pCurrentActiveScene = _pEditorScene;
    m_rCamera = NEW CPerspectiveCamera(65.0f, fViewportWidth / fViewportHeight, 100.0f, 18000.0f);
    m_rCamera->m_tPosition.Set(0, 2000, 3000);

    m_rCameraControls = NEW COrbitControls(m_rCamera.GetEntityData(), fViewportWidth, fViewportHeight);
    m_rCameraControls->target.Set(0, 0, 0);
    m_rCameraControls->enableDamping = False;
    m_rCameraControls->dampingFactor = 0.25;
    m_rCameraControls->screenSpacePanning = False;
    m_rCameraControls->minDistance = 250;
    m_rCameraControls->maxDistance = 5000;
    m_rCameraControls->maxPolarAngle = TWO_PI;
    _pEditorScene->SetCamera(m_rCamera.GetEntityData());

    m_rCommandMgr = NEW CCommandMgr();
    m_rCommandMgr->Init( this );

    m_rTerrainObject = NEW CTerrainObject( _pEditorScene );
    m_rTerrainObject->SetSize( m_strResourceSpace.c_str(), nBlockCountX, nBlockCountY, fBlockSize );
    _pEditorScene->AddChild(m_rTerrainObject.GetEntityData());

    m_rTerrainGridHelperObject = NEW CTerrainGridHelperObject();
    m_rTerrainGridHelperObject->Init( m_rTerrainObject.GetEntityData(), 1 );
    _pEditorScene->AddChild(m_rTerrainGridHelperObject.GetEntityData());

    //
    m_rSceneObjectNavigationMap = NEW CSceneObjectNavigationMap();
    m_rSceneObjectNavigationMap->SetSize( m_rTerrainObject.GetEntityData(), nBlockCountX, nBlockCountY, fBlockSize, fObjectUnitSize );

    m_rNavigateionHelperObject = NEW CNavigateionHelperObject();
    m_rNavigateionHelperObject->Init( m_rSceneObjectNavigationMap.GetEntityData() );
    _pEditorScene->AddChild(m_rNavigateionHelperObject.GetEntityData());

    //////////////////////////////////////////////////////////////////////////////////
    m_rSceneObjectCfgMgr = NEW CSceneObjectConfigManager();
    m_rSceneObjectCfgMgr->Init( m_strResourceSpace.c_str() );

    m_rSceneObjectManager = NEW CSceneObjectManager( );
    m_rSceneObjectManager->Init( m_strResourceSpace.c_str(), _pEditorScene );

    SetOperator( EOT_NULL );
}
void CEditorSceneMgr::Final(){
    //CSceneDecorationManager::GetInterface()->Release();
    //CSceneUnitManager::GetInterface()->Release();
    m_rTerrainGridHelperObject.Detach();
    //m_rTerrainWaterObject.Detach();
    m_rTerrainObject.Detach();
    //m_rTerrainDecelObject.Detach();
    m_rCameraControls.Detach();
    m_rCamera.Detach();
    m_rEditorScene->Final();
    m_rEditorScene.Detach();
}
bool32 CEditorSceneMgr::Open( CTextureTarget* pTarget, float fViewportWidth, float fViewportHeight, CXML* pXML ){
    char _strTextBuff[1024];
    char _strSkyBoxBuff[256];
    m_pTextureTarget = pTarget;
    m_fWidth = fViewportWidth;
    m_fHeight = fViewportHeight;

    HXMLNODEHANDLE _hSceneHandle = pXML->GetNode( "bdgamemap");
    m_strResourceSpace = pXML->GetAttributeValue( _hSceneHandle,  "resourcespace", _strTextBuff );

    CEditorScene* _pEditorScene = NEW CEditorScene();
    m_rEditorScene = _pEditorScene;

    CScene::m_pCurrentActiveScene = _pEditorScene;
    m_rCamera = NEW CPerspectiveCamera(65.0f, fViewportWidth / fViewportHeight, 100.0f, 18000.0f);
    m_rCamera->m_tPosition.Set(0, 2000, 3000);

    m_rCameraControls = NEW COrbitControls(m_rCamera.GetEntityData(), fViewportWidth, fViewportHeight);
    m_rCameraControls->target.Set(0, 0, 0);
    m_rCameraControls->enableDamping = False;
    m_rCameraControls->dampingFactor = 0.25;
    m_rCameraControls->screenSpacePanning = False;
    m_rCameraControls->minDistance = 250;
    m_rCameraControls->maxDistance = 5000;
    m_rCameraControls->maxPolarAngle = TWO_PI;
    _pEditorScene->SetCamera(m_rCamera.GetEntityData());

    m_rCommandMgr = NEW CCommandMgr();
    m_rCommandMgr->Init( this );

    m_rTerrainObject = NEW CTerrainObject( _pEditorScene );
    m_rTerrainObject->Open( m_strResourceSpace.c_str(), pXML, _hSceneHandle );
    _pEditorScene->AddChild(m_rTerrainObject.GetEntityData());

    m_rTerrainGridHelperObject = NEW CTerrainGridHelperObject();
    m_rTerrainGridHelperObject->Init( m_rTerrainObject.GetEntityData(), 1 );
    _pEditorScene->AddChild(m_rTerrainGridHelperObject.GetEntityData());

    //
    m_rSceneObjectNavigationMap = NEW CSceneObjectNavigationMap();
    m_rSceneObjectNavigationMap->Open( m_rTerrainObject.GetEntityData(), pXML, _hSceneHandle );

    m_rNavigateionHelperObject = NEW CNavigateionHelperObject();
    m_rNavigateionHelperObject->Init( m_rSceneObjectNavigationMap.GetEntityData() );
    _pEditorScene->AddChild(m_rNavigateionHelperObject.GetEntityData());

    //////////////////////////////////////////////////////////////////////////////////
    m_rSceneObjectCfgMgr = NEW CSceneObjectConfigManager();
    m_rSceneObjectCfgMgr->Init( m_strResourceSpace.c_str() );

    m_rSceneObjectManager = NEW CSceneObjectManager( );
    m_rSceneObjectManager->Open( m_strResourceSpace.c_str(), _pEditorScene, m_rSceneObjectNavigationMap.GetEntityData(), pXML, _hSceneHandle );

    SetOperator( EOT_NULL );

    return False;
}
bool32 CEditorSceneMgr::Save( const char* strFileName ){
    char _strTextBuff[ 1024 ];
    CFileWriter _sFileWriter;
    if( _sFileWriter.OpenFile( Format( _strTextBuff, 1024, "%s/maps/%s", g_strResourceWorkPath, strFileName ) ) == False )
        return False;
    
    int32 _nTextLen = snprintf( _strTextBuff, 1024, "<bdgamemap type=\"BDGAMEEDITOR\" version=\"0.2\" resourcespace=\"%s\" skybox=\"%s\">\n",
                                m_strResourceSpace.c_str(), ""/*m_rEditorScene->GetSkyBox()->strGetFileName()*/ );
    _sFileWriter.AddText( _strTextBuff, _nTextLen );

    //_nTextLen = snprintf( _strTextBuff, 1024, "<scenename>%s</scenename>\n", m_strSceneName.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    //_nTextLen = snprintf( _strTextBuff, 1024, "<scenedesc>%s</scenedesc>\n", m_strSceneDesc.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    //_nTextLen = snprintf( _strTextBuff, 1024, "<sceneplayerdesc>%s</sceneplayerdesc>\n", m_strScenePlayerDesc.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    //_nTextLen = snprintf( _strTextBuff, 1024, "<sceneauthor>%s</sceneauthor>\n", m_strSceneAuthor.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    //_nTextLen = snprintf( _strTextBuff, 1024, "<launchscript>%s</launchscript>\n", m_strLaunchScriptFile.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    //_nTextLen = snprintf( _strTextBuff, 1024, "<datafiledir>%s</datafiledir>\n", m_strDataFileDir.c_str() );
    //_sFileWriter.AddText( _strTextBuff, _nTextLen );
    
    //terrain
    m_rTerrainObject->Save( &_sFileWriter );
    //navigation map
    m_rSceneObjectNavigationMap->Save( &_sFileWriter );

    m_rSceneObjectManager->Save( &_sFileWriter );

    //CUserDataBaseMgr::GetInterface()->Save( _sFileWriter );
    //m_pUserDataBase->Save( &_sFileWriter );
    _nTextLen = snprintf( _strTextBuff, 1024, "</bdgamemap>\n" );
    _sFileWriter.AddText( _strTextBuff, _nTextLen );
    _sFileWriter.CloseFile();

    return True;
}
void CEditorSceneMgr::Update( int32 nDelta, float fDelta ){
    m_rCameraControls->update();
    m_rEditorScene->Update( nDelta, fDelta );
}
void CEditorSceneMgr::Render(){
    m_rEditorScene->Render( m_pTextureTarget, m_fWidth, m_fHeight);
}
CRaycaster* CEditorSceneMgr::BuildMouseRaycaster(CRaycaster* pRaycaster, int32 x, int32 y ) {
    CCamera* _pCamera = m_rCamera.GetEntityData();
    CMatrix4* _pProjViewM = &_pCamera->m_pWorldProjectionInverse;
    float w = m_fWidth;
    float h = m_fHeight;
    CVector3 _pointerVector((x / w) * 2.0f - 1.0f, -((y / h) * 2.0f - 1.0f), 0.5f);
    CMatrix4* _pWPI = _pProjViewM;
    _pointerVector.ApplyMatrix4(_pWPI);
    CVector3 camPosition = _pCamera->m_tPosition;
    _pointerVector -= camPosition;
    _pointerVector.Normalize();
    pRaycaster->SetRay(camPosition, _pointerVector, 100.0f, 5000.0f);
    return pRaycaster;
}
CTerrainBlock* CEditorSceneMgr::GetSelectBlock( int32 x, int32 y, STerrainSelectedPosInfo* pSelectedPosInfo ){
    CTerrainBlock* _pResult = NULL;
    CRaycaster _sRayCaster;
    CRaycaster* _pRayCaster = BuildMouseRaycaster(&_sRayCaster, x, y);
    CTerrainObject* _pTerrainObject = m_rTerrainObject.GetEntityData();
    CTerrainBlock* _pBlock = _pTerrainObject->SelectBlock( _pRayCaster, pSelectedPosInfo->m_vPos );
    if(_pBlock){
        _pTerrainObject->GetCellPos( pSelectedPosInfo->m_vPos[0], pSelectedPosInfo->m_vPos[2], &pSelectedPosInfo->m_nCellX, &pSelectedPosInfo->m_nCellY );
    }
    return _pBlock;
}
void CEditorSceneMgr::SetLayerTexture( int32 nCellX, int32 nCellY, const char* strTextureFile, int32 nBrushType, int32 nBrushSize, int32 nBrushAlpha ){
    m_rTerrainObject->SetLayerTexture( nCellX, nCellY, strTextureFile, nBrushType, nBrushSize, nBrushAlpha );
}
void CEditorSceneMgr::SetLayerTextureInfo( int32 nCellX, int32 nCellY, CTexture* pTextureObj, int32 nBrushAlpha ){
    //m_rTerrainObject->SetLayerTextureInfo( nCellX, nCellY, pTextureObj, nBrushAlpha );
}
void CEditorSceneMgr::SetOperator( EOperatorType eOperatorType ){
    if (m_rCurOperator.IsAttached() ){
        if (m_rCurOperator->GetOperatorType() == eOperatorType) {
            return;
        }
        m_rCurOperator->Final();
        m_rCurOperator.Detach();
    }

    COperator* _pOperator = NULL;
    switch( eOperatorType ){
    case EOT_NULL:{
        _pOperator = NEW CTerrainNull0Operator(this);
    }break;
    case EOT_TERRAIN_SURFACE_EDIT:{
        _pOperator = NEW CTerrainSurfaceOperator( this );
    }break;
    case EOT_TERRAIN_COLOR_EDIT:{
        _pOperator = NEW CTerrainColorOperator( this );
    }break;
    case EOT_TERRAIN_HEIGHT_RISE_EDIT: {
        _pOperator = NEW CTerrainHeightRiseOperator(this);
    }break;
    case EOT_TERRAIN_HEIGHT_FALL_EDIT: {
        _pOperator = NEW CTerrainHeightFallOperator(this);
    }break;
    case EOT_TERRAIN_HEIGHT_FLAT_EDIT: {
        _pOperator = NEW CTerrainHeightFlatOperator(this);
    }break;
    case EOT_TERRAIN_HEIGHT_STEEP_EDIT: {
        _pOperator = NEW CTerrainHeightSteepOperator(this);
    }break;
    case EOT_TERRAIN_HEIGHT_SMOOTH_EDIT: {
        _pOperator = NEW CTerrainHeightSmoothOperator(this);
    }break;
    case EOT_OBJECT_EDIT:{
        _pOperator = NEW CSceneCorationOperator(this);
    }break;
    case EOT_UNIT_EDIT:{
        _pOperator = NEW CSceneUnitOperator(this);
    }break;
    case EOT_HELPER_EDIT:{
        _pOperator = NEW CSceneHelperOperator(this);
    }break;
    case EOT_NAVIGATIONMAP_EDIT:{
        _pOperator = NEW CNavigationMapEditOperator(this);
    }break;
    }

    _pOperator->Init();
    m_rCurOperator = _pOperator;
}
void CEditorSceneMgr::OnMouseEvent( EUIMouseMessage eMouseMessage, int32 x, int32 y, int32 z, int32 s){
    switch (eMouseMessage) {
    case UME_MOUSELDOWN: {
        if (g_bIsCtrlDown) {
            m_rCameraControls->onMouseDown(ORBIT, x, y);
        } else{
            if (m_rCurOperator->OnLMouseDown(x, y) == True) {
                if (m_rTerrainGridHelperObject.IsAttached()) {
                    m_rTerrainGridHelperObject->UpdateGeometry();
                }
                //if( m_rTerrainPathHelperObject.IsAttached() ){
                //    m_rTerrainPathHelperObject->UpdateGeometry();
                //}
            }
        }
    }break;
    case UME_MOUSELUP: {
        if (g_bIsCtrlDown) {
            m_rCameraControls->onMouseUp(ORBIT, x, y);
        } else{
            if( m_rCurOperator->OnLMouseUp( x, y ) == True) {
                if (m_rTerrainGridHelperObject.IsAttached()) {
                    m_rTerrainGridHelperObject->UpdateGeometry();
                }
                //if( m_rTerrainPathHelperObject.IsAttached() ){
                //    m_rTerrainPathHelperObject->UpdateGeometry();
                //}
            }
        }
    }break;
    case UME_MOUSEMOVE: {
        if (m_rCurOperator->OnMouseMove(x, y) == True) {
            if (m_rTerrainGridHelperObject.IsAttached()) {
                m_rTerrainGridHelperObject->UpdateGeometry();
            }
            //if( m_rTerrainPathHelperObject.IsAttached() ){
            //    m_rTerrainPathHelperObject->UpdateGeometry();
            //}
        }
         
        //float _vSceneCoords[3];
        //CTerrainBlock* _pTerrainBlock = GetSelectBlock( x, y, _vSceneCoords );
        //if( _pTerrainBlock ){
        //    CNotification::GetInterface()->SendMessage( SETSTATUSCOORDTEXT , 0, 0, _vSceneCoords);
        //    int32 _nBrushSize = CTerrainToolPanel::GetInterface()->GetSelectTerrainBrushSize();
        //    m_rTerrainActiveBlockHelperObject->UpdateActiveBlock( _pTerrainBlock->m_nCoordX, _pTerrainBlock->m_nCoordY, _nBrushSize );
        //}
        m_rCameraControls->onMouseMove(x, y);
    }break;
    case UME_MOUSERDOWN: m_rCameraControls->onMouseDown( _PAN, x, y );  break;
    case UME_MOUSERUP:   m_rCameraControls->onMouseUp(_PAN, x, y);  break;
    case UME_MOUSEWHEEL: m_rCameraControls->onMouseWheel(z); break;
    }
}
void CEditorSceneMgr::OnKeyEvent( EUIKeyMessage eKeyMessage, int32 nKey){
    if (eKeyMessage == UKE_KEYDOWN) {
        switch( nKey ){
        case LEFT:
        case UP:
        case RIGHT:
        case BOTTOM:{
            m_rCameraControls->handleKeyDown(nKey);
        }break;
        default:{
            m_rCurOperator->OnKeyDown( nKey );
        }break;
        }
    }else{ //UKE_KEYUP
        switch( nKey ){
        case LEFT:
        case UP:
        case RIGHT:
        case BOTTOM:{
        }break;
        default:{
            m_rCurOperator->OnKeyUp( nKey );
        }break;
        }
    }
}
float CEditorSceneMgr::GetTerrainHeight( float x, float y ){
    return m_rTerrainObject->GetBlockGeometryHeight( x, y );
}
void CEditorSceneMgr::SetVisibled( int32 nType, bool32 bVisibled ){
    switch( nType ){
    case 0:{
        m_rEditorScene->m_bTerrainLineHelperVisibled = bVisibled;
    }break;
    case 1:{
        m_rEditorScene->m_bSceneObjectVisibled = bVisibled;
    }break;
    }
}
void CEditorSceneMgr::SetSceneVisibleMode( int32 nMode ){
    if( nMode == 0 ){
        m_rEditorScene->m_bTerrainVisibled = True;
    }else{
        m_rEditorScene->m_bTerrainVisibled = False;
        m_rSceneObjectNavigationMap->UpdateGeometry();
        //m_rSceneObjectNavigationMap->UpdateTextureData();
    }
}